package cmpe451.group7.ourcity.beans;

import java.io.Serializable;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;

import org.primefaces.event.map.OverlaySelectEvent;
import org.primefaces.event.map.StateChangeEvent;
import org.primefaces.model.map.DefaultMapModel;
import org.primefaces.model.map.LatLng;
import org.primefaces.model.map.LatLngBounds;
import org.primefaces.model.map.MapModel;
import org.primefaces.model.map.Marker;
import org.springframework.util.StringUtils;

import cmpe451.group7.ourcity.dao.PlaceDAO;
import cmpe451.group7.ourcity.entity.Place;
import cmpe451.group7.ourcity.entity.Tag;
import cmpe451.group7.ourcity.entity.User;
import cmpe451.group7.ourcity.recommendation.FreebaseQuery;
import cmpe451.group7.ourcity.service.PlaceService;
import cmpe451.group7.ourcity.service.SearchService;
import cmpe451.group7.ourcity.service.UserService;
import cmpe451.group7.ourcity.util.Utils;

/**
 * Search bean for using in the search page of the web site.
 */
@ManagedBean
@ViewScoped
public class SearchBean implements Serializable {

	private static final long serialVersionUID = -3106395871039054455L;

	@ManagedProperty(value = "#{loginBean}")
	private LoginBean loginBean;

	@ManagedProperty(value = "#{placeService}")
	private PlaceService placeService;

	@ManagedProperty(value = "#{searchService}")
	private SearchService searchService;

	@ManagedProperty(value = "#{userService}")
	private UserService userService;

	@ManagedProperty(value = "#{placeDAO}")
	private PlaceDAO placeDAO;

	private MapModel map;
	private Marker marker;

	private String country;
	private String city;
	private String placeName;
	private boolean restrictToCurrentMap;
	private Date fromDate;
	private Date toDate;
	private List<Tag> selectedTags = new LinkedList<Tag>();

	private List<Place> searchResults;
	private Place selectedPlace;
	private boolean loggedIn;
	private LatLng northEast = new LatLng(41.087067, 29.047440);
	private LatLng southWest = new LatLng(41.083833, 29.041539);

	private User currentUser;

	public SearchBean() {
		map = new DefaultMapModel();
	}

	@PostConstruct
	public void initialize() {
		if (loginBean != null && loginBean.isLoggedIn()) {
			currentUser = userService.fullyLoadUser(loginBean.getUserID());
		} else {
			currentUser = null;
		}
	}

	/**
	 * Search the DB with the given restrictions.
	 * 
	 * Search results is updated by the result set of this operation before
	 * updating the view.
	 */
	public void search() {
		boolean mapRestriction = restrictToCurrentMap
				&& (northEast != null && southWest != null);

		boolean hasQueryField = !(Utils.allZero(placeName, city, country) && (selectedTags == null || selectedTags
				.isEmpty()));

		if (!(mapRestriction || hasQueryField)) {
			addMessage(new FacesMessage(FacesMessage.SEVERITY_WARN,
					"Search error",
					"Please enter a query field or choose a area from the map"));
			return;
		}

		if (mapRestriction) {
			searchResults = searchService.detailedSearch(placeName, country,
					city, fromDate, toDate, selectedTags, northEast.getLat(),
					northEast.getLng(), southWest.getLat(), southWest.getLng());
		} else {
			searchResults = searchService.detailedSearch(placeName, country,
					city, fromDate, toDate, selectedTags);
		}

		map.getMarkers().clear();

		double lat, lng;
		for (Place p : searchResults) {
			placeDAO.initialize(p.getTags());
			lat = p.getLatitude();
			lng = p.getLongitude();
			if (lat != 0 && lng != 0) {
				map.addOverlay(new Marker(new LatLng(lat, lng), p
						.getPlaceName()));
			}
		}

		loggedIn = loginBean.isLoggedIn();
	}

	public void addNewTags() {

	}

	public List<String> completeCountry(String query) {
		if (query.length() > 1)
			return FreebaseQuery.completeCountry(query);
		else
			return null;
	}

	public List<String> completeCity(String query) {
		if (query.length() > 1)
			if (StringUtils.isEmpty(country)) {
				return FreebaseQuery.completeCity(query);
			} else {
				return FreebaseQuery.completeCity(country, query);
			}
		else
			return null;
	}

	/**
	 * Update the map coordinates after map movement
	 */
	public void onStateChange(StateChangeEvent event) {
		LatLngBounds bounds = event.getBounds();

		northEast = bounds.getNorthEast();
		southWest = bounds.getSouthWest();
	}

	public Marker getMarker() {
		return marker;
	}

	public void onMarkerSelect(OverlaySelectEvent event) {
		marker = (Marker) event.getOverlay();
	}

	public String viewDetails(Place p) {
		return "details.xhtml?faces-redirect=true&placeId=" + p.getPlaceID();
	}

	public List<Tag> completeTags(String query) {
		return FreebaseQuery.completeTags(query);
	}

	public String isLiked(Place p) {
		if (currentUser != null) {
			return (currentUser.getLikedPlace().contains(p)) ? "true" : "false";
		} else
			return "false";
	}

	public List<Place> getSearchResults() {
		return searchResults;
	}

	public Place getSelectedPlace() {
		return selectedPlace;
	}

	public void setSelectedPlace(Place selectedPlace) {
		this.selectedPlace = selectedPlace;
	}

	public void addMessage(FacesMessage message) {
		FacesContext.getCurrentInstance().addMessage(null, message);
	}

	public MapModel getMap() {
		return map;
	}

	public void setMap(MapModel map) {
		this.map = map;
	}

	public void likePlace(Place p) {
		placeService.likePlace(currentUser, p);
	}

	public String getCountry() {
		return country;
	}

	public void setCountry(String country) {
		this.country = country;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	public String getPlaceName() {
		return placeName;
	}

	public void setPlaceName(String placeName) {
		this.placeName = placeName;
	}

	public Date getFromDate() {
		return fromDate;
	}

	public void setFromDate(Date fromDate) {
		this.fromDate = fromDate;
	}

	public Date getToDate() {
		return toDate;
	}

	public void setToDate(Date toDate) {
		this.toDate = toDate;
	}

	public LoginBean getLoginBean() {
		return loginBean;
	}

	public void setLoginBean(LoginBean loginBean) {
		this.loginBean = loginBean;
	}

	public List<Tag> getSelectedTags() {
		return selectedTags;
	}

	public void setSelectedTags(List<Tag> selectedTags) {
		this.selectedTags = selectedTags;
	}

	public boolean isLoggedIn() {
		return loggedIn;
	}

	public void setLoggedIn(boolean loggedIn) {
		this.loggedIn = loggedIn;
	}

	public PlaceService getPlaceService() {
		return placeService;
	}

	public void setPlaceService(PlaceService placeService) {
		this.placeService = placeService;
	}

	public PlaceDAO getPlaceDAO() {
		return placeDAO;
	}

	public void setPlaceDAO(PlaceDAO placeDAO) {
		this.placeDAO = placeDAO;
	}

	public boolean isRestrictToCurrentMap() {
		return restrictToCurrentMap;
	}

	public void setRestrictToCurrentMap(boolean restrictToCurrentMap) {
		this.restrictToCurrentMap = restrictToCurrentMap;
	}

	public SearchService getSearchService() {
		return searchService;
	}

	public void setSearchService(SearchService searchService) {
		this.searchService = searchService;
	}

	public User getCurrentUser() {
		return currentUser;
	}

	public void setCurrentUser(User currentUser) {
		this.currentUser = currentUser;
	}

	public UserService getUserService() {
		return userService;
	}

	public void setUserService(UserService userService) {
		this.userService = userService;
	}
}
